package com.larly.aop;

import cn.hutool.core.util.ObjUtil;
import com.larly.annotation.AuthCheck;
import com.larly.constants.UserConstants;
import com.larly.exception.ErrorCode;
import com.larly.exception.BusinessException;
import com.larly.model.domain.User;
import com.larly.model.enums.user.UserRoleEnum;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 鉴权注解的AOP切面
 * 实现基于@AuthCheck注解的权限校验
 */
@Aspect
@Component
@Slf4j
public class AuthAspect {

    /**
     * 切点：拦截所有被@AuthCheck注解标记的方法
     */
    @Pointcut("@annotation(com.larly.annotation.AuthCheck)")
    public void authCheckPointcut() {
    }

    /**
     * 环绕通知：执行权限校验
     */
    @Around("authCheckPointcut()")
    public Object doAuthCheck(ProceedingJoinPoint joinPoint) throws Throwable {
        // 1. 获取注解信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        AuthCheck authCheck = method.getAnnotation(AuthCheck.class);
        if (authCheck == null) {
            // 没有注解，直接执行
            return joinPoint.proceed();
        }

        // 2. 获取当前请求和登录用户
        HttpServletRequest request = getHttpServletRequest();
        User loginUser = getLoginUser(request);

        // 3. 校验是否必须登录
        if (authCheck.mustLogin() && ObjUtil.isNull(loginUser)) {
            log.warn("访问方法[{}]需要登录，但用户未登录", method.getName());
            throw new BusinessException(ErrorCode.NOT_LOGIN, "请先登录");
        }

        // 4. 校验角色权限（如果指定了需要的角色）
        String[] requiredRoles = authCheck.requiredRoles();
        if (requiredRoles.length > 0 && ObjUtil.isNotNull(loginUser)) {
            boolean hasPermission = false;
            // 检查用户角色是否在所需角色列表中
            for (String requiredRole : requiredRoles) {
                if (requiredRole.equals(loginUser.getRole())) {
                    hasPermission = true;
                    break;
                }
            }
            // 管理员默认拥有所有权限
            if (!hasPermission && UserRoleEnum.ADMIN.getKey().equals(loginUser.getRole())) {
                hasPermission = true;
            }
            if (!hasPermission) {
                log.warn("用户[{}]访问方法[{}]权限不足，需要角色：{}",
                        loginUser.getId(), method.getName(), String.join(",", requiredRoles));
                throw new BusinessException(ErrorCode.NO_AUTH, "没有操作权限");
            }
        }

        // 5. 权限校验通过，执行目标方法
        log.debug("用户[{}]访问方法[{}]权限校验通过",
                ObjUtil.isNotNull(loginUser) ? loginUser.getId() : "未登录用户", method.getName());
        return joinPoint.proceed();
    }

    /**
     * 从请求上下文获取HttpServletRequest
     */
    private HttpServletRequest getHttpServletRequest() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes instanceof ServletRequestAttributes) {
            return ((ServletRequestAttributes) requestAttributes).getRequest();
        }
        log.warn("无法获取HttpServletRequest");
        return null;
    }

    /**
     * 从Session获取登录用户
     */
    private User getLoginUser(HttpServletRequest request) {
        if (request == null) {
            return null;
        }
        return (User) request.getSession().getAttribute(UserConstants.USER_LOGIN_STATE);
    }
}
